package com.qing.collector;

import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;

/**
 * 收集器，它将前n个自然数划分为质数和非质数
 */
public class PrimeNumbersCollector implements Collector<Integer, Map<Boolean, List<Integer>>, Map<Boolean, List<Integer>>> {

	@Override
	public Supplier<Map<Boolean, List<Integer>>> supplier() {
		return ()->new HashMap<Boolean, List<Integer>>(){
			{
				put(true,new ArrayList<Integer>());
				put(false,new ArrayList<Integer>());
			}
		};
	}

	@Override
	public BiConsumer<Map<Boolean, List<Integer>>, Integer> accumulator() {
		return (Map<Boolean,List<Integer>> acc,Integer candidate)->{
			//将已经找到的质数列表传递给isPrime方法
			//根据isPrime方法的返回值，从Map中区质数或非质数列表，把当前被测数加进去
			acc.get(isPrime(acc.get(true),candidate)).add(candidate);
		};
	}

	@Override
	public BinaryOperator<Map<Boolean, List<Integer>>> combiner() {
		return (Map<Boolean,List<Integer>> map1,Map<Boolean,List<Integer>> map2)->{
			//将第二个Map合并到第一个中
			map1.get(true).addAll(map2.get(true));
			map1.get(false).addAll(map2.get(false));
			return  map1;
		};
	}

	@Override
	public Function<Map<Boolean, List<Integer>>, Map<Boolean, List<Integer>>> finisher() {
		return Function.identity(); //收集过程最后无需转换，一次用identity函数收尾
	}

	@Override
	public Set<Characteristics> characteristics() {
		//这个收集器时IDENTITY_FINISH，但既不是 UNORDERED,IDENTITY_FINISH，因为质数是按顺序发现的
		/**
		 * Collections.unmodifiableSet()返回指定集合的不可修改视图。这种方法允许模块为用户提供对内部集合的“只读”访问。
		 */
		return Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH));
	}

	/**
	 * 是否是质数
	 * @param primes
	 * @param candidate
	 * @return
	 */
	public static boolean isPrime(List<Integer> primes,int candidate){
		int candidateRoot=(int)Math.sqrt((double) candidate);
		return takeWhile(primes,i->i<candidateRoot).stream().noneMatch(p->candidate%p==0);
	}

	/**
	 * 自定义Java9 中的takeWhile方法
	 * @param list
	 * @param p
	 * @param <A>
	 * @return
	 */
	public static <A> List<A> takeWhile(List<A> list, Predicate<A> p){
		int i=0;
		for (A item : list) {
			if(!p.test(item)){ //检查列表中的当前元素是否符合谓词的约束
				return  list.subList(0,i);
			}
			i++;
		}
		return list;
	}
}
